package org.duang.db.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import org.bson.types.BSONTimestamp;
import org.duang.db.IdEntity;
import org.duang.db.mongo.Operator;
import org.duang.kit.MongoKit;
import org.duang.kit.ToolsKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;

/**
 * Mongodb的查询对象
 * @author laotang
 */
public class Query {

	private final static Logger logger = LoggerFactory.getLogger(Query.class);
	
	private DBObject queryObj;
	private Order order;
	private Field field;
	private Page page;
	
	public Query(){
		queryObj = new BasicDBObject();
		order = new Order();
		field = new Field();
		page = new Page(0,1);
	}
	
	/**
	 * 等于
	 * @param key		字段名
	 * @param value		内容值
	 * @return
	 */
	public Query eq(String key, Object value){
		append2DBObject(key, null, value);
        return this;
	}
	
	/**
	 * 不等于
	 * @param key		字段名
	 * @param value		内容值
	 * @return
	 */
	public Query ne(String key, Object value){
		append2DBObject(key, Operator.NE, value);
        return this;
	}
	
	/**
	 * 大于(>)
	 * @param key		字段名
	 * @param value		内容值
	 * @return
	 */
	public Query gt(String key, Object value){
		append2DBObject(key, Operator.GT, value);
		return this;
	}
	
	/**
	 *  大于等于(>=)
	  * @param key		字段名
	 * @param value		内容值
	 * @return
	 */
	public Query gte(String key, Object value){
		append2DBObject(key, Operator.GTE, value);
		return this;
	}
	
	/**
	 * 小于(<)
	 * @param key		字段名
	 * @param value		内容值
	 * @return
	 */
	public Query lt(String key, Object value){
		append2DBObject(key, Operator.LT, value);
		return this;
	}
	
	/**
	 * 小于等于(<=)
	 * @param key		字段名
	 * @param value		内容值
	 * @return
	 */
	public Query lte(String key, Object value){
		append2DBObject(key, Operator.LTE, value);
		return this;
	}
	
	/**
	 * in查询
	 * @param key		字段名
	 * @param value		内容集合
	 * @return
	 */
	public Query in(String key, Object... value) {
		append2DBObject(key, Operator.IN, value);
		return this;
	}
	
	/**
	 * not in 查询
	 * @param key		字段名
	 * @param value		内容集合
	 * @return
	 */
	public Query nin(String key, Object... value) {
		append2DBObject(key, Operator.NIN, value);
		return this;
	}
	
	/**
	 * 多条件or查询
	 * @param querys	条件
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Query or( Query... querys) {
		List orDboList = (List)queryObj.get(Operator.OR);
		if(ToolsKit.isEmpty(orDboList)) {
			orDboList = new ArrayList();
			queryObj.put(Operator.OR, orDboList);
		}		
		for( Query q : querys ) {
			orDboList.add(q.getQueryObj());	
		}
		return this;
	}
	
	/**
	 * 多条件and查询
	 * @param querys	条件
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Query and(Query... querys) {
		List andDboList = (List)queryObj.get(Operator.AND);
		if(ToolsKit.isEmpty(andDboList)) {
			andDboList = new ArrayList();
			queryObj.put(Operator.AND, andDboList);
		}
		for(Query q : querys) {
			andDboList.add(q.getQueryObj());
		}
		return this;
	}
	
	/**
	 * 模糊查询
	 * @param key		字段名
	 * @param value		内容值
	 * @return
	 */
	public Query like(String key, Object value) {
		return regex(key, ".*"+value+".*");
	}
	
	/**
	 * 正则表达式查询
	 * @param key		字段名
	 * @param value		正则表达式字符串
	 * @return
	 */
	public Query regex(String key, String value) {
		append2DBObject(key, null, Pattern.compile(value));
		return this;
	}
	/**
	 * 对内嵌文档的多个键进行查询
	 * @param key		内嵌文档字段名
	 * @param query		查询对象
	 * @return
	 */
	public Query em(String key, Query query) {
		DBObject dbo = new BasicDBObject(Operator.ELEMMATCH, query.getQueryObj());
		queryObj.put(key, dbo);
		return this;
	}
	
	private void append2DBObject(String key, String oper, Object value){
		if(ToolsKit.isEmpty(key)) throw new NullPointerException("query key is null...");
//		if(ToolsUtil.isEmpty(value) && !Operator.NE.equals(oper) && oper == null) throw new NullPointerException("query value is null...");
		if(IdEntity.ID_FIELD.equals(key)){
			value = ToolsKit.message2ObjectId((String)value);
		}
		append(key, oper, value);
	}
	
	private void append2DBObject(String key, String oper, Object... value){
		if(ToolsKit.isEmpty(key)) throw new NullPointerException("query key is null...");
//		if(ToolsUtil.isEmpty(value)) throw new NullPointerException("query value is null...");
		if(IdEntity.ID_FIELD.equals(key)){
			append(key, oper, MongoKit.toObjectIds(value));
		} else {
			append(key, oper, value);
		}
	}
	
	private void append(String key, String oper, Object value) {
		if(ToolsKit.isEmpty(oper)){
			queryObj.put(key, value);		//如果没有操作符的话就全部当作等于查找
		} else {
			Object obj = queryObj.get(key);
			DBObject dbo = null;
			if(obj instanceof DBObject){
				((DBObject)obj).put(oper, value);				//追加到原来的dbo对象		
			} else {
				dbo = new BasicDBObject(oper, value);
				queryObj.put(key, dbo);
			}
		}
	}
	
	public Query fields(Field field) {
		this.field = field;
		return this;
	}
	
	@Deprecated
	public Query fields(Collection<String> fields) {
		for(Iterator<String> it = fields.iterator();it.hasNext();){
			field.add(it.next());
		}
		return this;
	}
	
	public Query order(String fieldName, String order) {
		this.order.add(fieldName, order);
		return this;
	}
	
	public Query order(Order order) {
		this.order = order;
		return this;
	}
	
	@SuppressWarnings("unchecked")
	public Query page(Page page){
		this.page = page;
		return this;
	}
	
	@Deprecated
	public Query pageNo(int pageNo){
		this.page.setPageNo(pageNo);
        return this;
    }
	@Deprecated
    public Query pageSize(int pageSize){
    	this.page.pageSize(pageSize);
        return this;
    }
	
  
    
	public DBObject getQueryObj() {
		logger.debug(" query: " + queryObj.toString());
		return queryObj;
	}

	public DBObject getDBOrder() {
		DBObject orderObj = order.getDBOrder();		
		logger.debug(" order: " + orderObj.toString());
		return orderObj;
	}
	
	public DBObject getDBFields() {
		DBObject fieldObj = field.getDBFields();
		logger.debug(" fields: " + fieldObj.toString());
		return fieldObj;
	}
	
	public Page getPage() {
		return page;
	}

}
